
#include "EditorCommands.h"

#include <iostream>
#include <cstring>
#include <thread>

#define GLM_FORCE_RADIANS
#include <glm/gtx/quaternion.hpp>

#include "../BSP.h"
#include "globals.h"

#include "Editor.h"
#include "TextureWindow.h"

#include "../leak.h"

using namespace std;

class ImportCmd;
class LoadCmd;
class SaveCmd;
class CreateBoxBrushCmd;
class CreateBoxBrushBBCmd;
class AddMeshCmd;
class MoveBuilderBrushCmd;
class RotateSelectedCmd;
class FreezeTextureCmd;
class ResetTextureCmd;
class DeleteSelectedCmd;
class CopySelectedCmd;
class SetTextureCmd;
class GridSizeCmd;
class PointSnapCmd;
class SetModeCmd;
class ClearSelectionCmd;

template <typename T, typename U>
class create_map
{
private:
    std::map<T, U> m_map;
public:
    create_map(const T& key, const U& val)
    {
        m_map[key] = val;
    }

    create_map<T, U>& operator()(const T& key, const U& val)
    {
        m_map[key] = val;
        return *this;
    }

    operator std::map<T, U>()
    {
        return m_map;
    }
};

map<string, Command*(*)(string)> commands = create_map<string, Command*(*)(string)>	("ImportFile",				CreateCommand<ImportCmd>)
																					("LoadFile", 				CreateCommand<LoadCmd>)
																					("SaveFile",				CreateCommand<SaveCmd>)
																					("CreateBoxBrush", 			CreateCommand<CreateBoxBrushCmd>)
																					("CreateBoxBrushBB", 		CreateCommand<CreateBoxBrushBBCmd>)
																					("AddMesh", 				CreateCommand<AddMeshCmd>)
																					("MoveBuilderBrush", 		CreateCommand<MoveBuilderBrushCmd>)
																					("RotateSelected", 			CreateCommand<RotateSelectedCmd>)
																					("FreezeTexture", 			CreateCommand<FreezeTextureCmd>)
																					("ResetTexture", 			CreateCommand<ResetTextureCmd>)
																					("DeleteSelected", 			CreateCommand<DeleteSelectedCmd>)
																					("CopySelected", 			CreateCommand<CopySelectedCmd>)
																					("SetTexture", 				CreateCommand<SetTextureCmd>)
																					("GridSize", 				CreateCommand<GridSizeCmd>)
																					("PointSnap", 				CreateCommand<PointSnapCmd>)
																					("SetMode", 				CreateCommand<SetModeCmd>)
																					("ClearSelection", 			CreateCommand<ClearSelectionCmd>);

list<shared_ptr<Command>> undo_history;
list<shared_ptr<Command>> redo_history;


void RunCommand(string line)
{
	if(line.length() < 3)
	{
		return;
	}
	char line_copy[100];
	strcpy(line_copy, line.c_str());
	string com = strtok(line_copy, " ");
	if(com == "Undo")
	{
		UndoCommand();
		return;
	}else
	if(com == "Redo")
	{
		RedoCommand();
		return;
	}
	char* params = strtok(NULL, "\0");
	auto create_proc = commands.find(com);
	if(create_proc == commands.end())
	{
		cout << "Command not found!" << endl;
		return;
	}
	cout << com << endl;
	if(params != nullptr)
	{
		undo_history.emplace_back((*create_proc).second(params));
	}else
	{
		undo_history.emplace_back((*create_proc).second(""));
	}
	undo_history.back()->Execute();
	if(undo_history.size() > 16)
	{
		undo_history.pop_front();
	}
}

void UndoCommand()
{
	if(undo_history.empty())
	{
		cout << "no commands to undo" << endl;
		return;
	}
	redo_history.push_back(undo_history.back());
	undo_history.back()->Undo();
	undo_history.pop_back();
}

void RedoCommand()
{
	if(redo_history.empty())
	{
		cout << "no commands to redo" << endl;
		return;
	}
	undo_history.push_back(redo_history.back());
	redo_history.back()->Execute();
	redo_history.pop_back();
}

vector<char*> Tokenize(char* str, const char* delims) {
	vector<char*> toks;
	char* tok = strtok(str, delims);
	while(tok != NULL)
	{
		toks.push_back(tok);
		tok = strtok(NULL, delims);
	}
	return toks;
}

static float Round(float val, float eps){
	int i_val = static_cast<int>(val / eps);
	float diff = val - static_cast<float>(i_val * eps);
	if(diff > eps/2.0f)
	{
		i_val++;
	} else
	{
		if(diff < -eps/2.0f)
		{
			i_val--;
		}
	}
	return  static_cast<float>(i_val * eps);
}

class ImportCmd : public Command
{
	string file_name_;
public:
	ImportCmd(string name) : file_name_(name){}
	void Execute()
	{
		lock_guard<mutex> render_lock(g_render_state.render_mtx);
		ifstream in_file(file_name_);
		if (!in_file)
		{
			cout << "Can't open file!  (\"" << file_name_ << "\")" << endl;
			return;
		}
		int num_brushes = 0;
		vector<string> texture_names;
		vector<float> tex_vecs;
		tex_vecs.reserve(512 * 11);

		list<Brush> new_brushes;

		string str;
		while(getline(in_file, str))
		{
			if(str.find("Number of brushes") != string::npos)
			{

			} else
			if(str.find("textures") != string::npos)
			{
				while(str.find("{") == string::npos)
				{
					getline(in_file, str);
				}
				getline(in_file, str);

				while(str.find("}") == string::npos)
				{
					char cstr[128];
					strcpy (cstr, str.c_str());
					char * p = strtok (cstr, " ()[]\t\n\r");

					texture_names.push_back(p);
					//Renderer::LoadTexture(p);
					getline(in_file, str);
				}
			} else
			if(str.find("brush") != string::npos)
			{
				while(str.find("{") == string::npos)
				{
					getline(in_file, str);
				}
				getline(in_file, str);

				int sides_index = 0;

				vector<float> sides;
				sides.reserve(256 * 3);

				while(str.find("}") == string::npos)
				{
					if(str.find("side") != string::npos)
					{
						char cstr[128];
						strcpy (cstr, str.c_str());

						char * p = strtok (cstr, " ()[]\t");

						for(int i = 0; i < 9; i++)
						{
							p = strtok(NULL, " ()[]\t");
							//sides[sides_index] = atof(p);
							sides.push_back(stof(p));
							//cout << p << endl;
							sides_index++;
						}
						for(int i = 0; i < 11; i++)
						{
							p = strtok(NULL, " ()[]\t");
							tex_vecs.push_back(stof(p));
						}

					}

					getline(in_file, str);
				}
				sides.shrink_to_fit();
				new_brushes.emplace_back(sides);
				num_brushes++;
			} else
			if(str.find("static_mesh") != string::npos)
			{
				while(str.find("{") == string::npos)
				{
					getline(in_file, str);
				}
				getline(in_file, str);

				g_static_meshes.emplace_back();

				char cstr[128];

				while(str.find("}") == string::npos)
				{
					if(str.find("name") != string::npos)
					{
						strcpy (cstr, str.c_str());
						char * p = strtok (cstr, " ()[]\t\r");
						p = strtok(NULL, " ()[]\t\r");

						g_static_meshes[g_static_meshes.size() - 1].set_name(p);
					} else
					if(str.find("file") != string::npos)
					{
						strcpy (cstr, str.c_str());
						char * p = strtok (cstr, " ()[]\t\r");
						p = strtok(NULL, " ()[]\t\r");

						g_static_meshes[g_static_meshes.size() - 1].LoadFromFile(p);
					} else
					if(str.find("angles") != string::npos)
					{
						glm::vec3 axis;
						strcpy (cstr, str.c_str());
						char * p = strtok (cstr, " ()[]\t");

						p = strtok(NULL, " ()[]\t");
						axis[0] = atof(p);
						p = strtok(NULL, " ()[]\t");
						axis[1] = atof(p);
						p = strtok(NULL, " ()[]\t");
						axis[2] = atof(p);

						float angle;
						p = strtok(NULL, " ()[]\t");
						angle = atof(p);
						//angles = glm::radians(angles);
						angle = glm::radians(angle);

						glm::quat q = glm::angleAxis(angle, axis);

						g_static_meshes[g_static_meshes.size() - 1].SetMatrix(glm::toMat4(q));
						//g_static_meshes[g_static_meshes.size() - 1].SetAngles(angles[0], angles[1], angles[2]);

					} else
					if(str.find("pos") != string::npos)
					{
						glm::vec3 pos;
						strcpy (cstr, str.c_str());
						char * p = strtok (cstr, " ()[]\t");

						p = strtok(NULL, " ()[]\t");
						pos[0] = atof(p);
						p = strtok(NULL, " ()[]\t");
						pos[1] = atof(p);
						p = strtok(NULL, " ()[]\t");
						pos[2] = atof(p);

						//g_static_meshes[g_static_meshes.size() - 1].Move(pos[0], pos[1], pos[2]);
						g_static_meshes[g_static_meshes.size() - 1].SetPos(pos[0], pos[1], pos[2]);
					}
					getline(in_file, str);
				}
			} else
			if(str.find("sky_camera") != string::npos)
			{
				g_entities.emplace_back(new SkyCamera());
				g_entities[g_entities.size()-1]->Import(in_file);
			}
		}

		//g_num_brushes = num_brushes;
		vector<thread> threads;
		int num_threads = min(thread::hardware_concurrency(), new_brushes.size());
		for(int i = 0;  i < num_threads; i++)
		{
			threads.push_back(thread([&](int id)->void
					{
						auto brush_it = new_brushes.begin();
						for(int k = 0; k < id; k++)
						{
							brush_it++;
						}
						for(; brush_it != new_brushes.end();)
						{
							(*brush_it).InitializeMesh();
							(*brush_it).UpdateUVs();
							for(int k = 0; k < num_threads && brush_it != new_brushes.end(); k++)
							{
								brush_it++;
							}
							if(brush_it == new_brushes.end())
							{
								break;
							}
						}
					}, i));
		}
		for(int i = 0;  i < num_threads; i++)
		{
			threads[i].join();
		}
		auto vec_it = tex_vecs.begin();
		for(auto brush_it = new_brushes.begin(); brush_it != new_brushes.end(); brush_it++)
		{
			(*brush_it).SetTexVec(vec_it, texture_names);
			(*brush_it).UpdateUVs();
		}
		in_file.close();
		g_brushes.insert(g_brushes.end(), 	std::make_move_iterator(new_brushes.begin()),
											std::make_move_iterator(new_brushes.end()));
	}
	void Undo()
	{
		cout << "Import is not undo-able :-/" << endl;
	}
};

class LoadCmd : public ImportCmd
{
public:
	LoadCmd(string name) : ImportCmd(name){}
	void Execute()
	{
		g_brushes.clear();
		g_static_meshes.clear();
		g_entities.clear();
		ImportCmd::Execute();
	}
	void Undo()
	{
		cout << "Load is not undo-able :-/" << endl;
	}
};

class SaveCmd : public Command
{
	string file_name_;
public:
	SaveCmd(string name) : file_name_(name){}
	void Execute()
	{
		ofstream out_file(file_name_);
		if ( !out_file )
		{
			cout << "Cant write file!  ( \"" << out_file << "\" )" << endl;
			return;
		}

		set<shared_ptr<GLTexture>> used_textures;
		for(auto& p_brush : g_brushes)
		{
			p_brush.GetUsedTextures(used_textures);
		}

		out_file << "Number of brushes = " << g_brushes.size() << endl;

		out_file << "textures" << endl;
		out_file << "{" << endl;

		/*for(auto texture_name_it = Renderer::texture_names.begin();
				 texture_name_it != Renderer::texture_names.end(); texture_name_it++)
		{
			out_file << "\t" << (*texture_name_it) << endl;
		}*/
		for(auto texture_it = used_textures.begin();
				 texture_it != used_textures.end(); texture_it++)
		{
			out_file << "\t" << Renderer::GetTextureName(*texture_it) << endl;
		}

		out_file << "}" << endl;

		for( auto brush = g_brushes.begin(); brush != g_brushes.end(); brush++ )
		{
			out_file << "brush" << endl << "{" << endl;

			const vector<Poly>& polys = (*brush).polygons();

			for( auto poly = polys.begin(); poly != polys.end(); poly++ )
			{
				out_file << "\tside ";

				out_file << "( " << Round((*poly).points[0], epsilon) << " " << Round((*poly).points[1], epsilon) << " " << Round((*poly).points[2], epsilon) << " ) ";
				out_file << "( " << Round((*poly).points[3], epsilon) << " " << Round((*poly).points[4], epsilon) << " " << Round((*poly).points[5], epsilon) << " ) ";
				out_file << "( " << Round((*poly).points[6], epsilon) << " " << Round((*poly).points[7], epsilon) << " " << Round((*poly).points[8], epsilon) << " ) ";
				out_file << "[ " << Round((*poly).tex_vec[0][0], epsilon) << " " << Round((*poly).tex_vec[0][1], epsilon) << " " << Round((*poly).tex_vec[0][2], epsilon) << " " << Round((*poly).tex_vec[0][3], epsilon) << " ] ";
				out_file << "[ " << Round((*poly).tex_vec[1][0], epsilon) << " " << Round((*poly).tex_vec[1][1], epsilon) << " " << Round((*poly).tex_vec[1][2], epsilon) << " " << Round((*poly).tex_vec[1][3], epsilon) << " ] ";
				//out_file << (*poly).tex_angle << " " << (*poly).tex_id << " " << (*poly).smoothing_group;
				out_file << (*poly).tex_angle << " " << distance(used_textures.begin(), used_textures.find((*poly).tex)) << " " << (*poly).smoothing_group;
				out_file << endl;
			}

			out_file << "}" << endl;
		}

		for(auto& st_mesh : g_static_meshes)
		{
			out_file << "static_mesh" << endl << "{" << endl;

			out_file << "\tname\t" << st_mesh.name() << endl;
			out_file << "\tfile\t" << st_mesh.p_mesh()->file_name() << endl;

			glm::mat4x4 mat = st_mesh.model_matrix();
			glm::quat q = glm::quat_cast(mat);
			//glm::dvec3 angles = glm::degrees(glm::eulerAngles(q));
			glm::vec3 axis = glm::axis(q);
			axis[0] = Round(axis[0], epsilon);
			axis[1] = Round(axis[1], epsilon);
			axis[2] = Round(axis[2], epsilon);
			axis = glm::normalize(axis);
			float angle = glm::degrees(glm::angle(q));
			angle = Round(angle, epsilon);
			//glm::vec3 angles = glm::degrees(glm::an(mat));
			out_file << "\tangles\t" << axis[0] << " " << axis[1] << " " << axis[2] << " " << angle << endl;
			out_file << "\tpos\t\t" << mat[3][0] << " " << mat[3][1] << " " << mat[3][2] << endl;

			out_file << "}" << endl;
		}

		for(auto& p_ent : g_entities)
		{
			string out_str;
			p_ent->Export(out_str);
			out_file << out_str;
		}
		out_file.close();
	}
	void Undo()
	{
		cout << "Save is not undo-able :-/" << endl;
	}
};

class CreateBoxBrushCmd : public Command
{
protected:
	float box_[6];
	Brush new_brush_;
public:
	CreateBoxBrushCmd(){}
	CreateBoxBrushCmd(string str)
	{
		vector<char*> params;
		Tokenize(const_cast<char*>(str.c_str()), const_cast<char*>(" ,")).swap(params);
		if(params.size() != 6)
		{
			cout << "Wrong dims!!!" << endl;
			box_[0] = 0.0f;		box_[1] = 0.0f;		box_[2] = 0.0f;
			box_[3] = 16.0f;	box_[4] = 16.0f;	box_[5] = 16.0f;
			return;
		}
		for(unsigned int i = 0; i < params.size(); i++)
		{
			box_[i] = stof(params[i]);
		}
	}
	void Execute()
	{
		vector<float> sides;
		sides.reserve(18 * 3);

		sides.push_back(box_[0]);		sides.push_back(box_[1]);		sides.push_back(box_[2]);
		sides.push_back(box_[0]);		sides.push_back(box_[4]);		sides.push_back(box_[2]);
		sides.push_back(box_[3]);		sides.push_back(box_[1]);		sides.push_back(box_[2]);

		sides.push_back(box_[0]);		sides.push_back(box_[1]);		sides.push_back(box_[2]);
		sides.push_back(box_[0]);		sides.push_back(box_[4]);		sides.push_back(box_[2]);
		sides.push_back(box_[0]);		sides.push_back(box_[1]);		sides.push_back(box_[5]);

		sides.push_back(box_[0]);		sides.push_back(box_[1]);		sides.push_back(box_[2]);
		sides.push_back(box_[3]);		sides.push_back(box_[1]);		sides.push_back(box_[2]);
		sides.push_back(box_[0]);		sides.push_back(box_[1]);		sides.push_back(box_[5]);

		sides.push_back(box_[3]);		sides.push_back(box_[4]);		sides.push_back(box_[5]);
		sides.push_back(box_[3]);		sides.push_back(box_[1]);		sides.push_back(box_[5]);
		sides.push_back(box_[0]);		sides.push_back(box_[4]);		sides.push_back(box_[5]);

		sides.push_back(box_[3]);		sides.push_back(box_[4]);		sides.push_back(box_[5]);
		sides.push_back(box_[3]);		sides.push_back(box_[1]);		sides.push_back(box_[5]);
		sides.push_back(box_[3]);		sides.push_back(box_[4]);		sides.push_back(box_[2]);

		sides.push_back(box_[3]);		sides.push_back(box_[4]);		sides.push_back(box_[5]);
		sides.push_back(box_[0]);		sides.push_back(box_[4]);		sides.push_back(box_[5]);
		sides.push_back(box_[3]);		sides.push_back(box_[4]);		sides.push_back(box_[2]);

		lock_guard<mutex> render_lock(g_render_state.render_mtx);

		new_brush_ = Brush(sides);
		new_brush_.InitializeMesh();
		new_brush_.UpdateUVs();
		new_brush_.SetTexture(Renderer::LoadTexture2("textures/checker.tga"));
		g_brushes.push_back(new_brush_);
	}
	void Undo()
	{
		auto brush_it = find(g_brushes.begin(), g_brushes.end(), new_brush_);
		if(brush_it != g_brushes.end())
		{
			g_brushes.erase(brush_it);
			return;
		}
	}
};

class CreateBoxBrushBBCmd : public CreateBoxBrushCmd
{
public:
	CreateBoxBrushBBCmd(string str)
	{
		box_[0] = g_builder_brush->bbox_min()[0];
		box_[1] = g_builder_brush->bbox_min()[1];
		box_[2] = g_builder_brush->bbox_min()[2];

		box_[3] = g_builder_brush->bbox_max()[0];
		box_[4] = g_builder_brush->bbox_max()[1];
		box_[5] = g_builder_brush->bbox_max()[2];
	}
};

class AddMeshCmd : public Command
{
	string file_name_;
	string mesh_name_;
public:
	AddMeshCmd(string str) :
		file_name_(str)
	{
	}
	void Execute()
	{
		g_static_meshes.push_back(GLStaticMesh());
		g_static_meshes[g_static_meshes.size()-1].LoadFromFile(file_name_);
		mesh_name_ = g_static_meshes.back().name();
	}
	void Undo()
	{
		for(auto mesh = g_static_meshes.begin(); mesh != g_static_meshes.end(); mesh++)
		{
			if((*mesh).name() == mesh_name_)
			{
				g_static_meshes.erase(mesh);
				return;
			}
		}
	}
};

template<int n>
class VecParamCmd : public Command
{
protected:
	int vec_dim_;
	vector<float> v_;
public:
	VecParamCmd(string str) :
		vec_dim_(n)
	{
		vector<char*> params;
		Tokenize(const_cast<char*>(str.c_str()), const_cast<char*>(" ,")).swap(params);

		v_.resize(n);
		if(params.size() != n)
		{
			cout << "Wrong parameters" << endl;
			return;
		}
		for(int i = 0; i < n; i++)
		{
			v_[i] = stof(params[i]);
		}
	}
};

class MoveBuilderBrushCmd : public VecParamCmd<3>
{
public:
	MoveBuilderBrushCmd(string str) :
		VecParamCmd(str)
	{
	}
	void Execute()
	{
		g_builder_brush->Move(&v_[0]);
	}
	void Undo()
	{
		glm::vec3 v(-v_[0], -v_[1], -v_[2]);
		g_builder_brush->Move(&v[0]);
	}
};

class RotateSelectedCmd : public VecParamCmd<4>
{
public:
	RotateSelectedCmd(string str) :
		VecParamCmd(str)
	{
	}
	void Execute()
	{
		g_selection_frame->Rotate(&v_[1], v_[0]);
	}
	void Undo()
	{
		g_selection_frame->Rotate(&v_[1], -v_[0]);
	}
};

class FreezeTextureCmd : public Command
{
	bool val_;
public:
	FreezeTextureCmd(string str)
	{
		if(str.length() == 0)
		{
			val_ = false;
		}
		if(strstr(str.c_str(), "on"))
		{
			val_ = true;
		}else
		{
			val_ = false;
		}
	}
	void Execute()
	{
		g_selection_frame->SetFreezeTexture(val_);
	}
	void Undo()
	{
		g_selection_frame->SetFreezeTexture(!val_);
	}
};

class ResetTextureCmd : public Command
{
public:
	ResetTextureCmd(string str)
	{
	}
	void Execute()
	{
		g_selection_frame->ResetTexture();
	}
	void Undo()
	{
		cout << "ResetTexture is not undo-able" << endl;
	}
};

class SnapShotCmd : public Command
{
protected:
	std::list<Brush> selection_;
	std::vector<shared_ptr<MovableObject>> selected_objects_;
	std::vector<shared_ptr<BaseEntity>> selected_entities_;
public:
	SnapShotCmd()
	{
	}
	SnapShotCmd(string str)
	{
	}
	void Execute()
	{
		std::set<Brush*> sel = g_selection_frame->selection();
		for(auto p_brush : sel)
		{
			selection_.push_back(*p_brush);
		}
		std::set<MovableObject*> sel_objs = g_selection_frame->selected_objects();
		for(auto p_obj : sel_objs)
		{
			selected_objects_.emplace_back(p_obj->Clone());
		}
		std::set<BaseEntity*> sel_ents = g_selection_frame->selected_entities();
		for(auto p_ent : sel_ents)
		{
			selected_entities_.emplace_back(p_ent->Clone());
		}
	}
	void Undo()
	{
		g_brushes.insert(g_brushes.end(), selection_.begin(), selection_.end());
		//g_static_meshes.insert(g_static_meshes.end(), selected_objects_.begin(), selected_objects_.end());
		//g_entities.insert(g_entities.end(), selected_entities_.begin(), selected_entities_.end());
		//for(auto p_ent : selected_entities_)
		//{

		//}
	}
};

class DeleteSelectedCmd : public SnapShotCmd
{
public:
	DeleteSelectedCmd(string str)
	{
	}
	void Execute()
	{
		SnapShotCmd::Execute();
		g_selection_frame->DeleteSelection();
	}
	void Undo()
	{
		SnapShotCmd::Undo();
	}
};

class CopySelectedCmd : public Command
{
public:
	CopySelectedCmd(string str)
	{
	}
	void Execute()
	{
		g_selection_frame->CopySelection();
	}
	void Undo()
	{
		cout << "CopySelected is not undo-able" << endl;
	}
};

class SetTextureCmd : public SnapShotCmd
{
	string tex_name_;
public:
	SetTextureCmd(string str) :
		tex_name_(str)
	{
	}
	void Execute()
	{
		SnapShotCmd::Execute();
		g_selection_frame->SetTexrure(Renderer::LoadTexture2(tex_name_.c_str()));
	}
	void Undo()
	{
		SnapShotCmd::Undo();
	}
};

class GridSizeCmd : public Command
{
	int size_, size_bfr_;
public:
	GridSizeCmd(string str) :
		size_bfr_(16)
	{
		vector<char*> params;
		Tokenize(const_cast<char*>(str.c_str()), const_cast<char*>(" ,")).swap(params);
		if(params.size() < 1)
		{
			cout << "Wrong parameters" << endl;
			size_ = 16;
			return;
		}
		size_ = atoi(params[0]);
	}
	void Execute()
	{
		size_bfr_ = g_render_state.grid_size;
		g_render_state.grid_size = size_;
	}
	void Undo()
	{
		g_render_state.grid_size = size_bfr_;
	}
};

class PointSnapCmd : public Command
{
	bool val_;
public:
	PointSnapCmd(string str)
	{
		vector<char*> params;
		Tokenize(const_cast<char*>(str.c_str()), const_cast<char*>(" ,")).swap(params);
		if( params.size() != 1 )
		{
			cout << "Wrong parameters" << endl;
		}
		if( strcmp( params[0], "on" ) == 0 )
		{
			val_ = true;
			g_render_state.point_snap = true;
		} else
		{
			if( strcmp( params[0], "off" ) == 0 )
			{
				val_ = false;
			} else
			{
				cout << "Wrong parameter" << endl;
				val_ = true;
			}
		}
	}
	void Execute()
	{
		g_render_state.point_snap = val_;
	}
	void Undo()
	{
		g_render_state.point_snap = !val_;
	}
};

class SetModeCmd : public Command
{
	int mode_, mode_bfr_;
public:
	SetModeCmd(string str) :
		mode_bfr_(0)
	{
		vector<char*> params;
		Tokenize(const_cast<char*>(str.c_str()), const_cast<char*>(" ,")).swap(params);
		if( params.size() != 1 )
		{
			cout << "Wrong parameters" << endl;
		}

		if( strcmp( params[0], "edit" ) == 0 )
		{
			mode_ = EDIT_MODE;
		} else
		{
			if( strcmp( params[0], "select" ) == 0 )
			{
				mode_ = SELECT_MODE;
			} else
			{
				if( strcmp( params[0], "texture" ) == 0 )
				{
					mode_ = TEX_MODE;
				} else
				{
					cout << "Wrong parameter" << endl;
				}
			}
		}
	}
	void Execute()
	{
		mode_bfr_ = g_selection_frame->mode();
		SwitchMode(mode_);
	}
	void Undo()
	{
		SwitchMode(mode_bfr_);
	}
private:
	void SwitchMode(int mode)
	{
		g_selection_frame->set_mode(mode);
		switch(mode)
		{
		case SELECT_MODE:
		case EDIT_MODE:
			g_tex_window->Hide();
			break;
		case TEX_MODE:
			g_tex_window->Show();
			break;
		}
	}
};

class ClearSelectionCmd : public Command
{
	std::set<Brush*> sel_;
	std::set<Poly*> sel_faces_;
	std::set<MovableObject*> sel_objs_;
	std::set<BaseEntity*> sel_ents_;
public:
	ClearSelectionCmd(string str)
	{

	}
	void Execute()
	{
		sel_ = g_selection_frame->selection();
		sel_faces_ = g_selection_frame->selected_faces();
		sel_objs_ = g_selection_frame->selected_objects();
		sel_ents_ = g_selection_frame->selected_entities();
		g_selection_frame->Clear();
	}
	void Undo()
	{
		for(auto p_brush : sel_)
		{
			g_selection_frame->Add(p_brush);
		}
		for(auto p_face : sel_faces_)
		{
			g_selection_frame->Add(p_face);
		}
		for(auto p_obj : sel_objs_)
		{
			g_selection_frame->Add(p_obj);
		}
		for(auto p_ent : sel_ents_)
		{
			g_selection_frame->Add(p_ent);
		}
	}
};
